/*
 * written by Alexey Matveev according to the lessons
 * https://vulkan-tutorial.com/
*/

#include <render/output/vkrender.h>
#include <render/output/window.h>
#include <vulkan/vulkan.h>
#include <app/Application.h>
#include <app/modules/logger.h>
#include <cstring> //for strcmp()
#include <iostream>

using namespace std;

bool isDeviceSuitable(VkPhysicalDevice device);

void vkrender::mainLoop() {
    
    while ( !window -> isClosed () ) {
        
        window -> Update ();
    }
}

void vkrender::initilization( Application& applicationRef, Window* window ) {
    app = &applicationRef;
    this -> window = window;
    
    VkInstanceCreateInfo InstanceInfo = {};
    VkApplicationInfo applicationInfo = {};

    applicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
    applicationInfo.pApplicationName = (*app).appName.c_str();
    applicationInfo.applicationVersion = 1;
    applicationInfo.pEngineName = "AME";
    applicationInfo.engineVersion = VK_MAKE_VERSION ( (*app).ameVersionMajor, (*app).ameVersionMinor, 
                                                      app -> ameversionCertificate );
    applicationInfo.apiVersion = VK_API_VERSION_1_0;
    
    InstanceInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
    InstanceInfo.pApplicationInfo = &applicationInfo;
    
    
    uint32_t glfwExtensionCount = 0;
    const char** glfwExtensions;
    
    glfwExtensions = glfwGetRequiredInstanceExtensions ( &glfwExtensionCount );
    
    InstanceInfo.enabledExtensionCount = glfwExtensionCount;
    InstanceInfo.ppEnabledExtensionNames = glfwExtensions;
    
    
    if ( vkCreateInstance( &InstanceInfo, nullptr, &this->instance ) == VK_SUCCESS ) {
        LOG_DEBUG("Vulkan is ready!");
    } else {
        LOG_DEBUG("Error!");
    }
    
    
    uint32_t extensionCount = 0;
    vkEnumerateInstanceExtensionProperties ( nullptr, &extensionCount, nullptr );
    std::vector < VkExtensionProperties > extensions ( extensionCount );
    
    vkEnumerateInstanceExtensionProperties ( nullptr, &extensionCount, extensions.data () );
    
    LOG_DEBUG ("available extensions:");
    for (const auto& extension : extensions) {
        LOG_DEBUG ( extension.extensionName );
    }
    

    uint32_t deviceCount = 0;
    vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
    
    this->devices.resize ( deviceCount );
    vkEnumeratePhysicalDevices ( this->instance, &deviceCount, this->devices.data () );
    
    if ( deviceCount == 0 ) {
        LOG_DEBUG ( "failed to find GPUs with Vulkan support!" );
    }
    
    
    for (const auto& device : this->devices) {
        
        if ( isDeviceSuitable ( device ) ) {
            physicalDevice = device;
            break;
        }
    }

    if ( physicalDevice == VK_NULL_HANDLE ) {
        
        LOG_DEBUG ( "failed to find a suitable GPU!" );
    }
    
    LOG_DEBUG ( "Main loop" );
    mainLoop ();
}

bool isDeviceSuitable ( VkPhysicalDevice device ) {
    
    VkPhysicalDeviceProperties deviceProperties;
    VkPhysicalDeviceFeatures deviceFeatures;
    
    vkGetPhysicalDeviceProperties(device, &deviceProperties);
    vkGetPhysicalDeviceFeatures(device, &deviceFeatures);

    return deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU && 
        deviceFeatures.geometryShader;
}
